#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <string.h>
#include <bits/getopt_core.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/time.h>

#define TIME_SUB_MS(tv1, tv2) ((tv1.tv_sec - tv2.tv_sec) * 1000 + (tv1.tv_usec - tv2.tv_usec) / 1000)

typedef struct test_context_s
{
    char serverip[16];
    int port;
    int threadnum;
    int connection;
    int requestion;
#if 1
    int failed;
#endif

} test_context_t;

int connec_tcp_server(char *ip, unsigned short port)
{
    int connfd = socket(AF_INET, SOCK_STREAM, 0);

    struct sockaddr_in tcpserver_addr;
    memset(&tcpserver_addr, 0, sizeof(struct sockaddr_in));

    tcpserver_addr.sin_family = AF_INET;
    tcpserver_addr.sin_addr.s_addr = inet_addr(ip);
    tcpserver_addr.sin_port = htons(port); // 错把htons写成htonl，一直不通，崩溃

    int ret = connect(connfd, (struct sockaddr *)&tcpserver_addr, sizeof(struct sockaddr_in));
    if (ret)
    {
        perror("connect");
        return -1;
    }

    return connfd;
}

int send_recv_tcppck(int fd)
{
    char recvbuf[128] = {0};
    char sendbuf[128] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890abcdefghijklmnopqrstuvwxyz\r\n";
    int sres = send(fd, sendbuf, strlen(sendbuf), 0);
    if (sres < 0)
    {
        perror("send failed\n");
        return -1;
    }

    int rres = recv(fd, recvbuf, 128, 0);
    if (rres < 0)
    {
        perror("recv failed\n");
        return -1;
    }
    return 0;
}

void *test_qps_entry(void *arg)
{
    test_context_t *pctx = (test_context_t *)arg;

    //-c 选项没做，可以使用过循环实现
    int clientfd = connec_tcp_server(pctx->serverip, pctx->port);
    if (clientfd < 0)
    {
        perror("connec_tcp_server failed\n");
        return NULL;
    }

    int count = pctx->requestion / pctx->threadnum;
    for (int i = 0; i < count; i++)
    {
        int srres = send_recv_tcppck(clientfd);
        if (srres != 0)
        {
            perror("send_recv_tcppck failed\n");
            pctx->failed++;
            continue;
        }
    }
    return NULL;
}

// 测试命令:./test_qps_client -s 192.168.100,200 -p 9999 -t 50 -c 100 -n 10000
//  -t:线程数
//  -c:连接数量connec_tcp_server
//  -n:请求数量send_recv_tcppck
int main(int argc, char *argv[])
{
    int res = 0;

    test_context_t ctx = {0};

    int opt = 0;
    while ((opt = getopt(argc, argv, "s:p:t:c:n:")) != -1)
    {
        switch (opt)
        {
        case 's':
            // printf("-s:%s\n",optarg);
            memcpy(ctx.serverip, optarg, 16);
            // printf("-s:%s\n",ctx.serverip);
            break;
        case 'p':
            // printf("-p:%s\n",optarg);
            ctx.port = atoi(optarg);
            printf("-p:%d\n", ctx.port);
            break;
        case 't':
            // printf("-t:%s\n",optarg);
            ctx.threadnum = atoi(optarg);
            break;
        case 'c':
            // printf("-c:%s\n",optarg);    //连接数量connec_tcp_server
            ctx.connection = atoi(optarg);
            break;
        case 'n':
            // printf("-n:%s\n",optarg);    //请求数量send_recv_tcppck
            ctx.requestion = atoi(optarg);
            break;
        default:
            break;
        }
    }

    pthread_t *ptid = malloc(ctx.threadnum * sizeof(pthread_t));

    struct timeval tv_begin;
    gettimeofday(&tv_begin, NULL);

    for (int i = 0; i < ctx.threadnum; i++)
    {
        pthread_create(&ptid[i], NULL, test_qps_entry, &ctx);
    }

    for (int j = 0; j < ctx.threadnum; j++)
    {
        pthread_join(ptid[j], NULL);
    }

    struct timeval tv_end;
    gettimeofday(&tv_end, NULL);

    int time_used = TIME_SUB_MS(tv_end, tv_begin);

    printf("success: %d, failed: %d, time_used: %d, qps: %d\n", ctx.requestion - ctx.failed, ctx.failed, time_used, ctx.requestion * 1000 / time_used);

    free(ptid);
    return 0;
}